#include "Param.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define ASSIGNED_NOTASSIGNED	0	//not assigned
#define ASSIGNED_DEFAULT		1	//default value
#define ASSIGNED_ASSIGNED		2	//assigned by application

static int argc;
static char **argv;
static TParam *paramList;
static int paramCount;

static int exeName;
static int lastSimple;

bool showError(int pos, char *text, char* textParam=NULL)
{
	int len;
	
	printf("\nERROR!\n%s ", &argv[0][exeName]);

	len = strlen(&argv[0][exeName]) + 1;
	for (int i=1; i<argc; i++)
	{
		printf("%s ", argv[i]);
		if (i < pos)
			len += strlen(argv[i])+1;
	}
	printf("\n");
	for (int i=0;i<len;i++)
		printf(" ");
	printf("^\n");

	if (textParam)
		printf(text, textParam);
	else
		printf(text);
	printf("\n\n");

	return false;
}

void printDesc(char *param, char *desc, int pad, bool minus)
{
	int len = strlen(param);

	if (minus)
	{
		printf("-");
		len++;
	}
	printf("%s", param);

	int p = 0;
	int lineCount = 1;
	while (1)
	{
		for (int i=len; i<pad;i++)
		{
			printf(" ");
		}
		len = 0;

		while (desc[p] && desc[p] != '\n')
			printf("%c", desc[p++]);
		
		printf("\n");

		if (desc[p] == 0)
		{
			if (lineCount>1)
				printf("\n");
			return;
		}
		p++;
		lineCount++;
	}
}

void showUsage(char *extra)
{
	printf("\nUsage: ");
	printf("\n%s ", &argv[0][exeName]);

	int len = 0;
	for (int i=0;i<paramCount;i++)
	{
		int l = 0;
		l = strlen(paramList[i].name);

		if (l>len)
			len = l;
	}

	len += 3;

	for (int i=0;i<paramCount;i++)
	{
		if (paramList[i].required)
			printf("<");
		else
			printf("[");
		if (paramList[i].type != ptype_simple)
		{
			printf("-%s", paramList[i].name);
			if (paramList[i].type != ptype_switch)
				printf(" %s", paramList[i].param);
		}
		else
		{
			printf("%s", paramList[i].name);
		}
		if (paramList[i].required)
			printf("> ");
		else
			printf("] ");
	}
	printf("\n\n");

	for (int i=0;i<paramCount;i++)
	{
		printDesc(paramList[i].name, paramList[i].description, len, paramList[i].type != ptype_simple);
	}

	printf("\n\n");

	if (extra)
		printf("%s\n\n", extra);
}

bool parseCommand(int _argc, char **_argv, TParam *_paramList, int _paramCount, char* extra)
{
	//copy the params
	argc = _argc;
	argv = _argv;
	paramList = _paramList;
	paramCount = _paramCount;

	lastSimple = -1;

	//find out the exe name
	exeName = 0;
	for (int i=0;argv[0][i];i++)
	{
		if ((argv[0][i] == '\\')||(argv[0][i] == '/'))
			exeName = i+1;
	}

	//if no params, show the help
	if (argc == 1)
	{
		showUsage(extra);
		return false;
	}

	//scan all params
	for (int i=1; i<argc; i++)
	{
		//do we need to show help?
		if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "/h") || !strcmp(argv[i], "-?") || !strcmp(argv[i], "/?"))
		{
			showUsage(extra);
			return false;
		}

		//do we have a param?
		if (argv[i][0] == '-')
		{
			//yup, let's search
			int p = -1;
			for (int j=0;j<paramCount;j++)
			{
				if ((paramList[j].type != ptype_simple) && !strcmp(paramList[j].name, &argv[i][1]))
				{
					p = j;
					break;
				}
			}
			//if found a match
			if (p>=0)
			{
				//if already used
				if (paramList[p].assigned == ASSIGNED_ASSIGNED)
					return showError(i, "Param \"-%s\" already used", paramList[p].name);

				paramList[p].assigned = ASSIGNED_ASSIGNED;

				//if it's not a switch, read next param
				if (paramList[p].type != ptype_switch)
				{
					i++;
					if (i==argc)
						return showError(i, "Expecting argument after \"-%s\"", paramList[p].name);

					if (paramList[p].type == ptype_string)
						paramList[p].strValue = argv[i];

					if (paramList[p].type == ptype_int)
					{
						char *endPtr;
						//convert to int
						paramList[p].intValue = (int)strtol(argv[i], &endPtr, 0);
						if (*endPtr)
						{
							//conversion stopped before string's end
							return showError(i, "Invalid integer value \"%s\"", argv[i]);
						}
					}
				}
			}
			else
			{
				return showError(i, "Unknown param \"%s\"", argv[i]);
			}
		}
		else
		{	
			bool found = false;
			//nope, it's the default
			for (int j=0;j<paramCount;j++)
			{
				if ((paramList[j].type == ptype_simple) && (j > lastSimple))
				{
					lastSimple = j;
					found = true;

					paramList[j].strValue = argv[i];
					paramList[j].assigned = ASSIGNED_ASSIGNED;

					break;
				}
			}

			if (!found)
				return showError(i, "Too many params", argv[i]);

		}
	}

	//finished scanning, now see if all required parameters are present
	for (int i=0;i<paramCount;i++)
	{
		if (paramList[i].required && (paramList[i].assigned == ASSIGNED_NOTASSIGNED))
		{
			if (paramList[i].type != ptype_simple)
				return showError(argc, "Expecting param \"-%s\"", paramList[i].name);
			else
				return showError(argc, "Expecting param \"%s\"", paramList[i].name);
		}
	}
	return true;	
}

TParam param_simple(char *name, char *description, bool required)
{
	TParam p;

	p.name = name;
	p.param = NULL;
	p.description = description;
	p.type = ptype_simple;
	p.required = required;
	p.strValue = NULL;
	p.assigned = ASSIGNED_NOTASSIGNED;

	return p;
}

TParam param_switch(char *name, char *description)
{
	TParam p;

	p.name = name;
	p.param = NULL;
	p.description = description;
	p.type = ptype_switch;
	p.required = false;
	p.intValue = 0;
	p.assigned = ASSIGNED_NOTASSIGNED;

	return p;
}

TParam param_normal(char *name, char *param, char *description, EParamType type)
{
	TParam p;

	p.name = name;
	p.param = param;
	p.description = description;
	p.type = type;
	p.required = true;
	p.intValue = 0;
	p.assigned = ASSIGNED_NOTASSIGNED;

	return p;
}

TParam param_normal(char *name, char *param, char *description, int defaultVal)
{
	TParam p;

	p.name = name;
	p.param = param;
	p.description = description;
	p.type = ptype_int;
	p.required = false;
	p.intValue = defaultVal;
	p.assigned = ASSIGNED_DEFAULT;

	return p;
}

TParam param_normal(char *name, char *param, char *description, char* defaultVal)
{
	TParam p;

	p.name = name;
	p.param = param;
	p.description = description;
	p.type = ptype_string;
	p.required = false;
	p.strValue = defaultVal;
	p.assigned = ASSIGNED_DEFAULT;

	return p;
}